<template>
    <div v-loading="isLoading" :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
        <textarea :id="tinymceId" class="tinymce-textarea" />
    </div>
</template>

<script>
/**
 * docs:
 * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
 */
import plugins from './plugins'
import toolbar from './toolbar'
import load from './dynamicLoadScript'
import { remakeUrl } from '@/utils'
import { catchImgs } from '@/api/admin/upload'

// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
// const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.5/tinymce.min.js'
const tinymceCDN = './static/model/tinymce/tinymce.min.js'

export default {
    name: 'Tinymce',
    props: {
        id: {
            type: String,
            default: function() {
                return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
            }
        },
        value: {
            type: String,
            default: ''
        },
        toolbar: {
            type: Array,
            required: false,
            default() {
                return []
            }
        },
        menubar: {
            type: String,
            default: 'file edit insert view format table'
        },
        height: {
            type: [Number, String],
            required: false,
            default: 360
        },
        width: {
            type: [Number, String],
            required: false,
            default: 'auto'
        }
    },
    data() {
        return {
            isLoading: true,
            hasChange: false,
            hasInit: false,
            tinymceId: this.id,
            fullscreen: false,
            isPaste: false,
            languageTypeList: {
                'en': 'en',
                'zh': 'zh_CN',
                'es': 'es_MX',
                'ja': 'ja'
            }
        }
    },
    computed: {
        language() {
            return this.languageTypeList[this.$store.getters.language]
        },
        containerWidth() {
            const width = this.width
            if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
                return `${width}px`
            }
            return width
        }
    },
    watch: {
        value(val) {
            if (!this.hasChange && this.hasInit) {
                this.$nextTick(() =>
                    window.tinymce.get(this.tinymceId).setContent(val || ''))
            }
        },
        language() {
            this.destroyTinymce()
            this.$nextTick(() => this.initTinymce())
        }
    },
    mounted() {
        this.init()
    },
    activated() {
        if (window.tinymce) {
            this.initTinymce()
        }
    },
    deactivated() {
        this.destroyTinymce()
    },
    destroyed() {
        this.destroyTinymce()
    },
    methods: {
        init() {
            // dynamic load tinymce from cdn
            load(tinymceCDN, (err) => {
                if (err) {
                    this.$message.error(err.message)
                    return
                }
                this.initTinymce()
            })
        },
        initTinymce() {
            const _this = this
            window.tinymce.init({
                selector: `#${this.tinymceId}`,
                language: this.language,
                // language: this.languageTypeList['zh'],
                height: this.height,
                body_class: 'panel-body tinymce-body-custom',
                content_css: './static/custom.css',
                branding: false,
                object_resizing: false,
                toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
                menubar: this.menubar,
                plugins: plugins,
                end_container_on_empty_block: true,
                powerpaste_word_import: 'clean',
                code_dialog_height: 450,
                code_dialog_width: 1000,
                advlist_bullet_styles: 'square',
                advlist_number_styles: 'default',
                imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
                default_link_target: '_blank',
                paste_webkit_styles: true,
                link_title: false,
                nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
                init_instance_callback: editor => {
                    _this.isLoading = false;
                    console.log('初始化', Boolean(_this.value));
                    if (_this.value) {
                        editor.setContent(_this.value)
                    }
                    _this.hasInit = true
                    // NodeChange ->可能导致 赋值 慢于 渲染 不修改 content
                    editor.on('Change KeyUp SetContent', () => {
                        console.log('第一次方法');
                        this.hasChange = true
                        this.$emit('input', editor.getContent())
                    })
                    // 粘贴抓取图片
                    editor.on('paste', (a, b, c, d, e) => {
                        console.log(a, b, c, d, e);
                        this.isPaste = true
                    })
                    // editor.on('SetContent', () => {
                    //     console.log('第二个方法');
                    //     this.hasChange = true
                    //     this.$emit('input', editor.getContent())
                    //     if (this.isPaste) {
                    //         this.isPaste = false
                    //         const remoteImages = [];
                    //         // 获取所有img标签
                    //         const doc = editor.getDoc()
                    //         const items = doc.getElementsByTagName('img');
                    //         console.log('标签集合', items);
                    //         if (items.length) {
                    //             for (const img of items) {
                    //                 const src = img.getAttribute('_src') || img.src || ''
                    //                 console.log(src);
                    //                 // 过滤白名单
                    //                 console.log(/^(https?|ftp):/img.test(src));
                    //                 console.log(!_this.imgUrlTest(src));
                    //                 if (/^(https?|ftp):/img.test(src) && !_this.imgUrlTest(src)) {
                    //                     remoteImages.push(src);
                    //                 }
                    //             }
                    //         }
                    //         console.log('待处理图片', remoteImages);
                    //
                    //         if (remoteImages.length) {
                    //             // 需要上传的图片，批量上传
                    //             _this.catchRemoteImage(remoteImages, {
                    //                 success: (res) => {
                    //                     // 这部分代码本来是直接修改编辑器内的dom就可以实现效果的, 但是那种方式无解, 只能现在这样替换原有img的dom才能更新编辑器
                    //                     // 貌似编辑器diff验证了dom节点是否相同, 如果相同则没有更新编辑器的内容
                    //                     // 希望有大佬能有更好的解决方案
                    //                     // 现在的遗憾就是在上传完成后替换为本地的图片后, 有一段加载图片的时间, 这时候编辑器内图片会留白一段时间, 留白时间取决于网速
                    //                     let item;
                    //                     let data;
                    //                     let _src;
                    //                     // let __src;
                    //                     const list = res.list;
                    //                     for (let i = 0; i < items.length; i++) {
                    //                         item = items[i]
                    //                         _src = item.getAttribute('_src') || item.src || '';
                    //                         for (let j = 0; j < list.length; j++) {
                    //                             data = list[j]
                    //                             if (_src === data.fileUrl && data.isSuccess) { // 抓取失败时不做替换处理
                    //                                 // __src = data.path;
                    //                                 // 无奈之举, 直接修改dom编辑器不更新内容, 但是替换dom可以更新
                    //                                 // 这里用到了jquery, 其他框架换个替换dom的方法
                    //                                 // $(``).replaceAll($(item));
                    //                                 // 下面给出一种从官方代码中参考来的方式
                    //                                 _this.replaceImage(item, data, editor); // 使用下面的新方法
                    //                                 break;
                    //                             }
                    //                         }
                    //                     }
                    //                     // 同步到textarea
                    //                     editor.save();
                    //                 },
                    //                 error: (data) => {
                    //                     // 上传出错回调
                    //                 }
                    //             });
                    //         }
                    //     }
                    // })
                },
                setup(editor) {
                    editor.on('FullscreenStateChanged', (e) => {
                        _this.fullscreen = e.state
                    })
                },
                // it will try to keep these URLs intact
                // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/
                // https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversions
                convert_urls: false,
                images_upload_url: remakeUrl('/upload/uploadFileTinymce')
                // 整合七牛上传
                // images_dataimg_filter(img) {
                //   setTimeout(() => {
                //     const $image = $(img);
                //     $image.removeAttr('width');
                //     $image.removeAttr('height');
                //     if ($image[0].height && $image[0].width) {
                //       $image.attr('data-wscntype', 'image');
                //       $image.attr('data-wscnh', $image[0].height);
                //       $image.attr('data-wscnw', $image[0].width);
                //       $image.addClass('wscnph');
                //     }
                //   }, 0);
                //   return img
                // },
                // images_upload_handler(blobInfo, success, failure, progress) {
                //   progress(0);
                //   const token = _this.$store.getters.token;
                //   getToken(token).then(response => {
                //     const url = response.data.qiniu_url;
                //     const formData = new FormData();
                //     formData.append('token', response.data.qiniu_token);
                //     formData.append('key', response.data.qiniu_key);
                //     formData.append('file', blobInfo.blob(), url);
                //     upload(formData).then(() => {
                //       success(url);
                //       progress(100);
                //     })
                //   }).catch(err => {
                //     failure('出现未知问题，刷新页面，或者联系程序员')
                //     console.log(err);
                //   });
                // },
            })
        },
        destroyTinymce() {
            const tinymce = window.tinymce.get(this.tinymceId)
            if (this.fullscreen) {
                tinymce.execCommand('mceFullScreen')
            }

            if (tinymce) {
                tinymce.destroy()
            }
        },
        setContent(value) {
            window.tinymce.get(this.tinymceId).setContent(value)
        },
        getContent() {
            window.tinymce.get(this.tinymceId).getContent()
        },
        imageSuccessCBK(arr) {
            arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))
        },
        // 检查url白名单
        imgUrlTest(url) {
            const localDomains = ['/static/upload-sunoye']
            if (url.indexOf(location.host) !== -1 || /(^\.)|(^\/)/.test(url)) {
                return !0;
            }
            // 白名单
            if (localDomains) {
                for (const domain of localDomains) {
                    if (url.indexOf(domain) !== -1) {
                        return !0;
                    }
                }
            }
            return !1;
        },
        // 重新上传
        catchRemoteImage(images, callback) {
            const data = [];
            images.forEach(item => {
                data.push({ fileUrl: item })
            })
            catchImgs(data).then((res) => {
                if (res.code == 200) {
                    callback.success(res.data);
                    return false;
                }
                callback.error(res.data);
            }).catch(res => {
                callback.error('error', res);
            });
        },
        // 抓取图片
        replaceImage(image, data, editor) {
            const each = function(xs, f) {
                for (let i = 0, len = xs.length; i < len; i++) {
                    const x = xs[i]
                    f(x, i);
                }
            };
            const map = function(xs, f) {
                const len = xs.length
                const r = new Array(len)
                for (var i = 0; i < len; i++) {
                    var x = xs[i];
                    r[i] = f(x, i);
                }
                return r;
            };
            const replaceString = function(content, search, replace) {
                let index = 0;
                do {
                    index = content.indexOf(search, index);
                    if (index !== -1) {
                        content = content.substring(0, index) + replace + content.substr(index + search.length);
                        index += replace.length - search.length + 1;
                    }
                } while (index !== -1);
                return content;
            };
            const replaceImageUrl = function(content, targetUrl, replacementUrl) {
                const replacementString = 'src="' + replacementUrl + '"' + (replacementUrl === '' ? ' data-placeholder="1"' : '');
                content = replaceString(content, 'src="' + targetUrl + '"', replacementString);
                return content;
            };
            const replaceUrlInUndoStack = function(targetUrl, replacementUrl) {
                each(editor.undoManager.data, function(level) {
                    if (level.type === 'fragmented') {
                        level.fragments = map(level.fragments, function(fragment) {
                            return replaceImageUrl(fragment, targetUrl, replacementUrl);
                        });
                    } else {
                        level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
                    }
                });
            };
            // const src = editor.convertURL(data.path, 'src');
            const attr = {
                'src': data.path
            }
            replaceUrlInUndoStack(image.src, data.path);
            editor.$(image).attr(attr).removeAttr('alt').removeAttr('data-mce-src');
        }
    }
}
</script>

<style lang="scss" scoped>
.tinymce-container {
    position: relative;
    line-height: normal;
}

.tinymce-container {
    ::v-deep {
        .mce-fullscreen {
            z-index: 10000;
        }
    }
}

.tinymce-textarea {
    visibility: hidden;
    z-index: -1;
}

.editor-custom-btn-container {
    position: absolute;
    right: 4px;
    top: 4px;
    /*z-index: 2005;*/
}

.fullscreen .editor-custom-btn-container {
    z-index: 10000;
    position: fixed;
}

.editor-upload-btn {
    display: inline-block;
}
</style>
